package com.aco.party.web.controller.system;

import com.aco.party.dao.custom.BaseInfoDao;
import com.aco.party.enums.MetaTypeEnum;
import com.aco.party.model.po.BaseInfo;
import com.aco.party.model.po.Meta;
import com.aco.party.model.vo.BaseInfoParam;
import com.aco.party.model.vo.BaseInfoVo;
import com.aco.party.model.vo.common.DataGridEntry;
import com.aco.party.model.vo.common.Page;
import com.aco.party.model.vo.common.PageParam;
import com.aco.party.model.vo.common.Response;
import com.aco.party.service.system.DictService;
import com.aco.party.service.system.MetaService;
import com.aco.party.util.DateUtil;
import com.aco.party.web.command.system.EditBaseInfoCommand;
import com.aco.party.web.validator.system.EditDictValidator;
import com.google.common.primitives.Ints;
import lombok.Data;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by flying on 3/10/2016 0010.
 */
@Controller
@RequestMapping("/system")
public class ManageBaseInfoController extends BaseController {
    Logger logger = LoggerFactory.getLogger(ManageBaseInfoController.class);

    @Resource
    private DictService dictService;

    @Resource
    private MetaService metaService;

    @Resource
    private BaseInfoDao baseInfoDao;

    private EditDictValidator validator = new EditDictValidator();

    private final String REDIRECTEDIT = "redirect:edit";

    public static Object getFieldValue(Object obj, String field) {
        Class<?> claz = obj.getClass();
        Field f = null;
        Object fieldValue = null;
        try {
            f = claz.getDeclaredField(field);
            f.setAccessible(true);
            fieldValue = f.get(obj);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            return fieldValue;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return fieldValue;
    }

    /**
     * 计算数值型
     * @param origin
     * @param argName
     * @return
     */
    private Map<Integer,Double> numberCalculate(int origin,String argName,List<BaseInfo> baseInfos){
        Map<Integer,Double> result = new HashedMap();
        Map<Integer,Integer> idValueMap = new HashedMap();
        List<Integer> values = new ArrayList<>();
        int sum = origin;
        for (BaseInfo baseInfo : baseInfos){
            Integer value = (Integer)getFieldValue(baseInfo,argName);
            idValueMap.put(baseInfo.getId(),value);
            values.add(value);
            sum += value;
        }
        values.add(origin);
        // 直接设置最大为 200，最小为0。
        int max = 200;//NumberUtils.max(Ints.toArray(values));
        int min = 0;//NumberUtils.min(Ints.toArray(values));
        // 取消归一化处理
//        double ai = (double)origin / (double)sum;
        for (Map.Entry<Integer,Integer> entry : idValueMap.entrySet()){
//            double bi = (double)entry.getValue() / (double)sum;
            result.put(entry.getKey(),1 - (double)Math.abs(origin - entry.getValue()) / (double)Math.abs(max - min));
            System.out.println(String.format("id : %s,argValue : %s, result : %s",entry.getKey(),argName,result.get(entry.getKey())));
        }
        return result;
    }

    private Map<Integer,Integer> switchCalculate(int origin,String argName,List<BaseInfo> baseInfos){
        Map<Integer,Integer> result = new HashedMap();
        Map<Integer,Integer> idValueMap = new HashedMap();
        for (BaseInfo baseInfo : baseInfos){
            Integer value = ((Byte)getFieldValue(baseInfo,argName)).intValue();
            idValueMap.put(baseInfo.getId(),value);
        }

        for (Map.Entry<Integer,Integer> entry : idValueMap.entrySet()){
            if(origin == entry.getValue())
                result.put(entry.getKey(),1);
            else{
                result.put(entry.getKey(),0);
            }

            System.out.println(String.format("id : %s,argValue : %s, result : %s",entry.getKey(),argName,result.get(entry.getKey())));
        }
        return result;
    }

    /**
     *
     * @param mxiMin 输入的参数Min
     * @param mxiMax 输入的参数 Max
     * @param argName 要匹配的 参数名称 6 个中的一个，外面会传递不同的参数，计算不同的值
     * @param baseInfos 数据库中所有数据列表
     * @return
     */
    private Map<Integer,Double> rangeCalculate(double mxiMin,double mxiMax,String argName,List<BaseInfo> baseInfos){
        Map<Integer,Double> result = new HashedMap();
        Map<Integer,Double[]> idValueMap = new HashedMap();
        // 这个 for 循环是为了获取该数据库中数据行该参数的 min 和 max,先存放到 map 里 ：idValueMap
        for (BaseInfo baseInfo : baseInfos){
            Integer min = (Integer)getFieldValue(baseInfo,argName + "Min");
            Integer max = (Integer)getFieldValue(baseInfo,argName + "Max");

            double argUnit = 100D;
            if(argName.equals("co")){
                argUnit = 10000D;
            }
            Double minD = min / argUnit;
            Double maxD = max / argUnit;
            idValueMap.put(baseInfo.getId(),new Double[]{minD,maxD});
        }

        double cxi = (mxiMin + mxiMax) /2;// 第 1 步 （计算 Cxi)

        for (Map.Entry<Integer,Double[]> entry : idValueMap.entrySet()){
            double myiMin = entry.getValue()[0];
            double myiMax = entry.getValue()[1];

            double cyi = (myiMin + myiMax) / 2; // 第 1 步 （计算 Cyi)

            //第 2 步 START
            if(new BigDecimal(cxi).compareTo(new BigDecimal(cyi)) > 0){
                double temp = mxiMin;
                mxiMin = myiMin;
                myiMin = temp;

                temp = mxiMax;
                mxiMax = myiMax;
                myiMax = temp;
            }
            //第 2 步 END

            // 第 3 步 START
            double pxi = mxiMin * 0.1D;
            double qxi = mxiMax * 0.1D;

            double pyi = myiMin * 0.1D;
            double qyi = myiMax * 0.1D;

            double xi = (qxi * myiMin + pyi * mxiMax) /(pyi + qxi);
            double yi = 1 - (xi - mxiMax) / qxi;
            // 第 3 步 END

            // 第 4 AND 5 步 START
            double Axiyi = 0;
            double simxiyi = 0;
            double axi = 0;
            double ayi = 0;
            if(yi > 0){
                axi = (2 * mxiMax + qxi - 2 * mxiMin + pxi) / 2;
                ayi = (2 * myiMax + qyi - 2 * myiMin + pyi) / 2;

                // 第 6 步 START
                if(yi > 0 && yi < 1){
                    Axiyi = (mxiMax + qxi - myiMin + pyi) * yi / 2;
                    // 第 6 步 END

                    // 第 7 步 AND 8,9 步 START
                }else if(new BigDecimal(mxiMax + qxi).compareTo(new BigDecimal(myiMax + qyi)) < 0
                        && new BigDecimal(mxiMax - pxi).compareTo(new BigDecimal(myiMax - pyi)) < 0){
                    Axiyi = (2 * mxiMax + qxi - 2 * myiMin + pyi) / 2;

                    // 第 7 步 AND 8,9 步 END
                }else{
                    // 第 10 步 START
                    Axiyi = Math.min(axi,ayi);
                    // 第 10 步 END
                }
                // 第 13 步 START
                simxiyi = Axiyi / (axi + ayi - Axiyi);
                // 第 13 步 END
            }
            // 第 4 AND 5 步 END

            //剩下的无关，只是数据结构方面的设计
            result.put(entry.getKey(),simxiyi);

            System.out.println(String.format("id : %s,argValue : %s, result : %s",entry.getKey(),argName,simxiyi));
        }
        return result;
    }

    @RequestMapping(value = {"/baseInfo", "/baseInfo/list"}, method = RequestMethod.GET)
    public String showManagePage(HttpServletResponse response,Model model) {
        List<Meta> metas = metaService.getByType(MetaTypeEnum.词条类目.getValue());
        model.addAttribute("cats",metas);
        return "system/manageBaseInfos";
    }

    @RequestMapping(value = "baseInfo/list", method = RequestMethod.POST)
    @ResponseBody
    public Page
            <BaseInfoVo> getListJson(HttpServletRequest request, DataGridEntry entry, BaseInfoParam param) throws InvocationTargetException, IllegalAccessException {
        Page <BaseInfoVo> page = new Page<>();
        entry.webParamConvert();
        List<BaseInfo> baseInfos = baseInfoDao.getList(new PageParam(entry.getOffset(), entry.getLimit()),"%" + param.getName() + "%");
        List<BaseInfoVo> baseInfoVos = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(baseInfos)){
            BaseInfoVo baseInfoVo;
            for (BaseInfo baseInfo : baseInfos){
                baseInfoVo = new BaseInfoVo();
                BeanUtils.copyProperties(baseInfoVo,baseInfo);
                baseInfoVo.setExplosion("(" + baseInfo.getExplosionMin() + "-" + baseInfo.getExplosionMax() + "]");
                baseInfoVo.setCo("(" + baseInfo.getCoMin() + "-" + baseInfo.getCoMax() + "]");
                baseInfoVo.setGas("(" + baseInfo.getGasMin() + "-" + baseInfo.getGasMax() + "]");
                baseInfoVo.setVentilation("(" + baseInfo.getVentilationMin() + "-" + baseInfo.getVentilationMax() + "]");
                baseInfoVo.setSecondaryExplosion("(" + baseInfo.getSecondaryExplosionMin() + "-" + baseInfo.getSecondaryExplosionMax() + "]");
                baseInfoVo.setCollapse("(" + baseInfo.getCollapseMin() + "-" + baseInfo.getCollapseMax() + "]");
                baseInfoVo.setCreateTimeStr(DateUtil.dateToString(baseInfo.getCreateTime()));
                baseInfoVos.add(baseInfoVo);
            }
        }
        page.setTotal(baseInfoDao.getTotal(param.getName()));
        page.setRows(baseInfoVos);
        return page;
    }

    @RequestMapping(value = {"/baseInfo/calculate"}, method = RequestMethod.GET)
    public String showCalculatePage(HttpServletResponse response,Model model) {
        List<Meta> metas = metaService.getByType(MetaTypeEnum.词条类目.getValue());
        model.addAttribute("cats",metas);
        return "system/calculateBaseInfos";
    }

    @Data
    class CaculateInfo implements Comparable{
        private Integer id;
        private String name;
        private Double similar;

        @Override
        public int compareTo(Object o) {
            CaculateInfo other = (CaculateInfo) o;
            return new BigDecimal(other.getSimilar()).compareTo(new BigDecimal(this.getSimilar()));
        }
    }
    @RequestMapping(value = "baseInfo/calculate", method = RequestMethod.POST)
    @ResponseBody
    public Page
            <CaculateInfo> getCalculateListJson(HttpServletRequest request, DataGridEntry entry, BaseInfo param) throws InvocationTargetException, IllegalAccessException {
        Page <CaculateInfo> page = new Page<>();
        if(param.getTrappedPersonNum() == null){
            return Page.getEmpty();
        }
        List<BaseInfo> baseInfos = baseInfoDao.getList(null,null);
        List<CaculateInfo> baseInfoVos = new ArrayList<>();

        if(CollectionUtils.isNotEmpty(baseInfos)){
            CaculateInfo caculateInfo;
            // 人数 计算
            Map<Integer,Double> trappedPersonNumSimilarMap = numberCalculate(param.getTrappedPersonNum(),"trappedPersonNum",baseInfos);

            /** 以下 6 个均是范围型的计算 **/
            Map<Integer,Double> explosionSimilarMap = rangeCalculate(param.getExplosionMin() / 100D,param.getExplosionMax() / 100D,"explosion",baseInfos);
            Map<Integer,Double> coSimilarMap = rangeCalculate(param.getCoMin() / 10000D,param.getCoMax() / 10000D,"co",baseInfos);
            Map<Integer,Double> gasSimilarMap = rangeCalculate(param.getGasMin() / 100D,param.getGasMax() / 100D,"gas",baseInfos);
            Map<Integer,Double> ventilationSimilarMap = rangeCalculate(param.getVentilationMin() / 100D,param.getVentilationMax() / 100D,"ventilation",baseInfos);
            Map<Integer,Double> collapseSimilarMap = rangeCalculate(param.getCollapseMin() / 100D,param.getCollapseMax() / 100D,"collapse",baseInfos);
            Map<Integer,Double> secondaryExplosionSimilarMap = rangeCalculate(param.getSecondaryExplosionMin() / 100D,
                    param.getSecondaryExplosionMax() / 100D,"secondaryExplosion",baseInfos);

            // 是 or 否计算
            Map<Integer,Integer> fireHazardSimilarMap = switchCalculate(param.getFireHazard().intValue(),"fireHazard",baseInfos);
            for (BaseInfo baseInfo : baseInfos) {
                // 对上述结果进行加权平均
                double similar = trappedPersonNumSimilarMap.get(baseInfo.getId()) * 0.1507 + explosionSimilarMap.get(baseInfo.getId()) * 0.128 +
                        coSimilarMap.get(baseInfo.getId()) * 0.1092 + gasSimilarMap.get(baseInfo.getId()) * 0.0956 +
                        ventilationSimilarMap.get(baseInfo.getId()) * 0.1427 + collapseSimilarMap.get(baseInfo.getId()) *0.1155 +
                        secondaryExplosionSimilarMap.get(baseInfo.getId()) * 0.1342 + fireHazardSimilarMap.get(baseInfo.getId()) * 0.1241;
                caculateInfo = new CaculateInfo();
                caculateInfo.setId(baseInfo.getId());
                caculateInfo.setName(baseInfo.getName());
                caculateInfo.setSimilar(similar);
                baseInfoVos.add(caculateInfo);
            }
        }
        Collections.sort(baseInfoVos);
        page.setTotal(baseInfos.size());
        page.setRows(baseInfoVos);
        return page;
    }

    @RequestMapping(value = "/baseInfo/edit", method = RequestMethod.GET)
    public String showEdit(HttpServletResponse response,@ModelAttribute EditBaseInfoCommand command, Model model) {
        if (command.getId() != null) {
            BaseInfo po = baseInfoDao.selectByPrimaryKey(command.getId());
            if (po != null)
                command.toCommand(po);
            else
                model.addAttribute("no_obj", true);
        }
        return "system/editBaseInfoDetail";
    }

    @RequestMapping(value = "/baseInfo/edit", method = RequestMethod.POST)
    public String edit(Model model, @ModelAttribute EditBaseInfoCommand command, BindingResult errors) {
        validator.validate(command, errors);
        if (!errors.hasErrors()) {
            BaseInfo baseInfo = new BaseInfo();
                command.toPo(baseInfo);
            baseInfo.setUpdateTime(new Date());
            baseInfoDao.updateByPrimaryKey(baseInfo);
            model.addAttribute("no_error",true);
        }
        return "system/editBaseInfoDetail";
    }

    @RequestMapping(value = "/baseInfo/add", method = RequestMethod.GET)
    public String showAdd(Model model,@ModelAttribute EditBaseInfoCommand command) {
        return "system/editBaseInfoDetail";
    }

    @RequestMapping(value = "/baseInfo/add", method = RequestMethod.POST)
    public String add(Model model, @ModelAttribute EditBaseInfoCommand command, BindingResult errors,RedirectAttributes attributes) {
//        validator.validate(command, errors);
        if (errors.hasErrors()) {
            return "system/editBaseInfoDetail";
        }
        BaseInfo baseInfo = new BaseInfo();
        command.toPo(baseInfo);

//        baseInfo.setCoMin(baseInfo.getCoMin() * 100);
//        baseInfo.setCoMax(baseInfo.getCoMax() * 100);
//        baseInfo.setExplosionMin(baseInfo.getExplosionMin() * 100);
//        baseInfo.setExplosionMax(baseInfo.getExplosionMax() * 100);
//        baseInfo.setGasMax(baseInfo.getGasMax() * 100);
//        baseInfo.setGasMin(baseInfo.getGasMin() * 100);
//        baseInfo.setVentilationMax(baseInfo.getVentilationMax() * 100);
//        baseInfo.setVentilationMin(baseInfo.getVentilationMin() * 100);
//        baseInfo.setCollapseMin(baseInfo.getCollapseMin() * 100);
//        baseInfo.setCollapseMax(baseInfo.getCollapseMax() * 100);
//        baseInfo.setSecondaryExplosionMin(baseInfo.getSecondaryExplosionMin() * 100);
//        baseInfo.setSecondaryExplosionMax(baseInfo.getSecondaryExplosionMax() * 100);
//        baseInfo.setCoMin(baseInfo.getCoMin() * 100);

        baseInfo.setCreateTime(new Date());
        baseInfo.setUpdateTime(new Date());
        baseInfoDao.insertSelective(baseInfo);
        attributes.addAttribute("id",baseInfo.getId());
        attributes.addFlashAttribute("no_error",true);
        return REDIRECTEDIT;
    }

    @RequestMapping(value = "/baseInfo/delete",method = RequestMethod.POST)
    @ResponseBody
    public Response del(@RequestParam(required = true) Long id){
        logger.info("删除：" + id);
        baseInfoDao.deleteByPrimaryKey(id.intValue());
        return new Response();
    }

}
